package com.ding.easycontroller.helper;

import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

public class EcBufferRequest extends HttpServletRequestWrapper implements EcRequest {

    private EcRequest core;

    public EcBufferRequest(EcRequest request) {
        super(request);
        this.core = request;
    }

    @Override
    public String getRequestURI() {
        return core.getRequestURI();
    }


    @Override
    public StringBuffer getRequestURL() {
        return core.getRequestURL();
    }

    @Override
    public String getPathInfo() {
        return core.getPathInfo();
    }


    private BodyHolder bodyHolder;


    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (bodyHolder == null) {
            bodyHolder = new BodyHolder();
            bodyHolder.fill(super.getInputStream());
        }

        InputStream inputStream = bodyHolder.getInputStream();

        if (inputStream == null) {
            return null;
        }
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                try {
                    return inputStream.available() > 0;
                } catch (IOException e) {
                    return true;
                }
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }

            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
        };
    }


    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
     * 获取byte数组类型的请求体
     *
     * @return
     */
    public byte[] getBateArrayBody() {
        try {
            ServletInputStream inputStream = getInputStream();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            StreamUtils.copy(inputStream, byteArrayOutputStream);
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 获取字符串类型的请求体
     *
     * @return
     */
    public String getStringBody() {
        byte[] bateArrayBody = getBateArrayBody();
        if (bateArrayBody == null) {
            return null;
        }
        return new String(bateArrayBody);
    }


    private static class BodyHolder {

        //只读取一次
        private byte[] buff;

        private File file;

        private List<Closeable> closeables = new ArrayList<>();


        //最大5m
        private static final int max = 1024 * 1024 * 5;

        private void fill(InputStream inputStream) {

            try {
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                int len;
                byte[] bytes = new byte[1024];

                boolean toFile = false;

                while ((len = inputStream.read(bytes)) != -1) {
                    byteArrayOutputStream.write(bytes, 0, len);
                    if (byteArrayOutputStream.size() > max) {
                        toFile = true;
                        break;
                    }
                }

                if (toFile) {
                    toFile(byteArrayOutputStream.toByteArray(), inputStream);
                } else {
                    this.buff = byteArrayOutputStream.toByteArray();
                }

            } catch (Exception ignore) {
            }
        }

        private void toFile(byte[] inMemory, InputStream inputStream) {
            try {
                file = File.createTempFile("ec-request-", ".tmp");
                FileOutputStream fileOutputStream = new FileOutputStream(file);
                //继续写入文件
                fileOutputStream.write(inMemory);
                int len;
                byte[] bytes = new byte[1024];
                while ((len = inputStream.read(bytes)) != -1) {
                    fileOutputStream.write(bytes, 0, len);
                }
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        @Override
        protected void finalize() throws Throwable {
            for (Closeable closeable : closeables) {
                closeable.close();
            }

            if (file != null) {
                file.delete();
            }
        }

        private InputStream getInputStream() {

            if (buff != null) {
                return new ByteArrayInputStream(buff);
            } else if (file != null) {
                try {
                    BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file));
                    closeables.add(bufferedInputStream);
                    return bufferedInputStream;
                } catch (FileNotFoundException e) {
                    return null;
                }
            }

            return null;

        }


    }

}
